import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean;

// Nikolas Wolfe
// 2/26/09
// Chat Chat

public class ChatClient
{
	// hostname, port, socket
	private final String host;
	private final int port;
	private Thread writer;
	private Thread reader;
	private final AtomicBoolean terminate = new AtomicBoolean(false);
	private Socket clientSocket;

	// establish input stream from keyboard
	private final InputStreamReader input = new InputStreamReader(System.in);
	private final BufferedReader userInput = new BufferedReader(input);
	
	public ChatClient(String hostname, int portNumber) throws IOException
	{
		this.host = hostname;
		this.port = portNumber;
		try
		{
			System.out.print("\nConnecting to server... ");
			this.clientSocket = new Socket(host, port);
			System.out.println("Done!\n");
		}
		catch( IOException e )
		{
			System.out.println("Initialization failed!\n");
			terminate.set(true); 
			throw new IOException();
		}
	}
	
	public void setup() 
	{
		try
		{
			// get user's name
			System.out.print("Enter your name: ");
			String name = userInput.readLine().toUpperCase();
			System.out.println("Thank you! Starting chat service...\n");

			this.writer = new Thread(new Writer(clientSocket, userInput, name));
			this.reader = new Thread(new Reader(clientSocket));

			writer.start(); // kick off the typing functionality
			reader.start(); // kick off the receiving functionality
		} 
		catch ( IOException e )
		{
			System.out.println("Chat initialization failed!\n");
			terminate.set(true); 
		}
	}
	
	public void tearDown()
	{
		System.out.println("Shutting down chat service... ");
		try
		{
			writer.interrupt();
			reader.interrupt();
			writer.join();
			reader.join();
		} catch (InterruptedException e) {}
		try
		{
			clientSocket.close(); // close the socket
		} catch (IOException e) {}
		System.out.println("Chat client terminated.");
	}

	private class Writer implements Runnable
	{
		private final DataOutputStream outStream;
		private final BufferedReader input;
		private final String name;
		private String data;
		private static final String exit = "exit";
		
		public Writer(Socket s, BufferedReader br, String name) throws IOException
		{
			this.outStream = new DataOutputStream(s.getOutputStream());
			this.input = br;
			this.name = name;
		}
		
		public void run()
		{
			while( !Thread.interrupted() && !terminate.get() )
			{
				try
				{
					System.out.print(toString() + ": ");
					data = input.readLine();
					if( data.trim().equals(exit) )
					{
						tearDown(); // kill everyone!
					}
					outStream.writeBytes( toString() + ": " + data + '\n' );
				}
				catch( Exception e ) // InterruptedException or IOException 
				{
					Thread.currentThread().interrupt();
					terminate.set(true);
				}
			}
			System.out.println("Writer terminated!");
		}
		
		public String toString()
		{
			return this.name;
		}
	}
	
	private class Reader implements Runnable
	{
		private final BufferedReader inStream;
		
		public Reader(Socket s) throws IOException
		{
			this.inStream = new BufferedReader(
					new InputStreamReader(s.getInputStream()));
		}
		
		public void run()
		{
			while( !Thread.interrupted() && !terminate.get() )
			{
				try
				{
					// read input from client
					System.out.println(inStream.readLine());
				} 
				catch ( Exception e ) // InterruptedException or IOException 
				{
					Thread.currentThread().interrupt();
				}
			}
			System.out.println("Reader terminated!");
		}
	}

}
